package com.zhou.base.components.feign.simple.feign.config;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.github.wnameless.json.flattener.JsonFlattener;
import com.zhou.base.components.bs.common.exception.base.BaseExceptionBuilder;
import com.zhou.base.components.bs.common.model.Response;
import com.zhou.base.components.bs.common.util.JSONUtils;
import com.zhou.base.components.feign.simple.feign.TestUrlFeign;
import com.zhou.base.components.feign.simple.util.TestGuavaCacheUtil;
import com.zhou.base.components.feign.simple.util.TestSignatureUtil;
import feign.Client;
import feign.FeignException;
import feign.QueryMapEncoder;
import feign.Request;
import feign.RequestInterceptor;
import feign.Util;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.zhou.base.components.bs.common.enums.ResponseEnum.SUCCESS;

/**
 * @author xianfeng
 * @date 2023/11/23 20:22
 * @description:
 */
public class TestFeignConfiguration {

    private static final String TEST_SYSTEM_CACHE_KEY = "test_system_cache_key";
    private final TestUrlFeign testUrlFeign;
    @Value("${testSys.appkey:testSystem1}")
    private String appKey;
    @Value("${testSys.appSecret:123456}")
    private String appSecret;

    public static void main(String[] args) throws NoSuchMethodException {
        String json = "{\"code\":\"0\",\"msg\":\"操作成功\",\"data\":\"abdafdasfa2wqeqezs\",\"success\":true}";
        Method mehtod = TestUrlFeign.class.getMethod("getToken", String.class, String.class);
        Type genericReturnType = mehtod.getGenericReturnType();
        //需要依赖getset方法
//        Object bean = JSONUtil.toBean(json, genericReturnType, false);
        Object object = JSONUtils.toObject(json, genericReturnType);
        System.out.println(object);
    }

    public TestFeignConfiguration(@Lazy TestUrlFeign testUrlFeign) {
        this.testUrlFeign = testUrlFeign;
    }

    @Bean
    public RequestInterceptor headerInterceptor() {
        return (requestTemplate) -> {
            String url = requestTemplate.url();
            if (!url.startsWith("/getToken")) {
                Map<String, Collection<String>> queries = requestTemplate.queries();
                //1.根据参数和秘钥生成签名调用第三方接口
                requestTemplate.header("appKey", appKey);
                Map<String, Collection<String>> queries1 = requestTemplate.queries();
                Collection<String> params = queries1.get("param");
                if (params != null) {
                    String param = params.stream().findFirst().get();
                    requestTemplate.header("sign", TestSignatureUtil.getSign(param, appSecret));
                }
                //2.根据第三方接口获取token,然后使用token访问接口，并将签名存储在redis中，拿不到后重新调用
                //这里利用本地缓存模拟
                String token = (String) TestGuavaCacheUtil.get(TEST_SYSTEM_CACHE_KEY, () -> {
                    Response<String> tokenRsp = testUrlFeign.getToken(appKey, appSecret);
                    return tokenRsp.getData();
                });
                requestTemplate.header("Authorization", token);
            }
        };
    }
/*    @Bean
    public QueryMapEncoder queryMapEncoder() {
        return new QueryMapEncoder() {
            @SneakyThrows
            @Override
            public Map<String, Object> encode(Object object) {
                Map<String, Object> maps = new HashMap<>();
                String json = JSONUtils.toJson(object);
                maps.put("biz_content", json);
                return maps;
            }
        };
    }*/

    @Bean
    public QueryMapEncoder queryMapEncoder() {
        return new QueryMapEncoder() {
            @Override
            public Map<String, Object> encode(Object object) {
                try {
                    return JsonFlattener.flattenAsMap(JSONUtil.toJsonStr(object));
                } catch (Throwable var3) {
                    throw var3;
                }
            }
        };
    }

    @Bean
    public Decoder feignDecoder() {
        return new Decoder() {
            @Override
            public Object decode(feign.Response response, Type type) throws IOException, DecodeException, FeignException {
                String bodyStr = Util.toString(response.body().asReader(Util.UTF_8));
                JSONObject jsonObject = JSONUtil.parseObj(bodyStr);
                if (!SUCCESS.getCode().equals(jsonObject.getInt("code"))) {
                    throw BaseExceptionBuilder.message("接口调用异常" + bodyStr);
                }
                Object object = JSONUtils.toObject(bodyStr, type);
                return object;
            }
        };
    }

    /**
     * 模拟一个需要特殊请求的client（如果第三方给了调用包直接调用，会经过一些复杂的转换）
     * 参考D:\maven-repository\io\github\openfeign\feign-okhttp\10.10.1\feign-okhttp-10.10.1.jar!\feign\okhttp\OkHttpClient.class
     *
     * @return
     */
    @Bean
    public Client csbClient() {
        return new Client() {
            @Override
            public feign.Response execute(Request input, Request.Options options) throws IOException {
                try (final CloseableHttpClient httpclient = HttpClients.createDefault()) {
                    //请求转换
                    BasicClassicHttpRequest http5Request = toHttp5Request(input);
                    //2.请求执行
                    CloseableHttpResponse execute = httpclient.execute(http5Request);
                    //3.响应转换
                    return toFeignResponse(execute, input);
                }
            }

            private BasicClassicHttpRequest toHttp5Request(feign.Request input) {
                BasicClassicHttpRequest apacheRequest = new BasicClassicHttpRequest(input.method(), input.url());
                ContentType contentType = null;
                boolean hasAcceptHeader = false;
                Iterator var4 = input.headers().keySet().iterator();
                while (var4.hasNext()) {
                    String field = (String) var4.next();
                    if (field.equalsIgnoreCase("Accept")) {
                        hasAcceptHeader = true;
                    }
                    Iterator var6 = ((Collection) input.headers().get(field)).iterator();

                    while (var6.hasNext()) {
                        String value = (String) var6.next();
                        apacheRequest.addHeader(field, value);
                        if (field.equalsIgnoreCase("Content-Type")) {
                            contentType = ContentType.parse(value);
                            if (input.charset() != null) {
                                contentType.withCharset(input.charset());
                            }
                        }
                    }
                }
                //http5，不允许设置Content-Length
                apacheRequest.removeHeaders("Content-Length");
                if (!hasAcceptHeader) {
                    apacheRequest.addHeader("Accept", "*/*");
                }
                byte[] inputBody = input.body();
                boolean isMethodWithBody = Request.HttpMethod.POST == input.httpMethod() || Request.HttpMethod.PUT == input.httpMethod() || Request.HttpMethod.PATCH == input.httpMethod();
                if (isMethodWithBody) {
                    if (inputBody == null) {
                        inputBody = new byte[0];
                    }
                }
                Header[] headers = apacheRequest.getHeaders("Content-Type");
                apacheRequest.removeHeaders("Content-Type");
                String value = null;
                if (headers.length > 0) {
                    value = headers[0].getValue();
                }
                if (inputBody != null) {
                    ByteArrayEntity entity = new ByteArrayEntity(inputBody, StrUtil.isNotBlank(value) ? ContentType.parse(value) : ContentType.APPLICATION_JSON);
                    apacheRequest.setEntity(entity);
                }
                return apacheRequest;
            }

            private feign.Response toFeignResponse(CloseableHttpResponse response, feign.Request request) throws IOException {
                try {
                    return feign.Response.builder().status(response.getCode()).reason(response.getReasonPhrase())
                            .request(request)
                            .headers(toMap(response.getHeaders()))
                            .body(EntityUtils.toString(response.getEntity()), CharsetUtil.CHARSET_UTF_8)
                            .build();
                } catch (ParseException e) {
                    throw new RuntimeException(e);
                }
            }

            private Map toMap(Header[] input) {
                LinkedHashMap<String, List<String>> collect = Arrays.stream(input)
                        .collect(Collectors.groupingBy(t -> t.getName(), LinkedHashMap::new, Collectors.mapping(Header::getValue, Collectors.toList())));
                return collect;
            }

        };
    }
}
